Function.RegisterNamespace("Test");

[Import("../../Source/Extensions.js/String.js")]
[Fixture]
Test.String=function(){
    [Fixture]
    function Concat(){
        [Fact]
        function ReturnsSingleString(){
            var expected="expected";

            var actual=String.Concat(expected);

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsMultipleValues(){
            var target="target";
            var expected=[target,target,target].join('');

            var actual=String.Concat(target,target,target);

            Assert.Equal(expected,actual);
        }

        [Fact]
        function SkipsUndefinedValues(){
            var target="target";
            var expected=[target,target,target].join('');

            var actual=String.Concat(target,null,target,undefined,target);

            Assert.Equal(expected,actual);
        }
    }

    [Fixture]
    function Contains(){
        [Fact]
        function ThrowsIfInputIsOmitted(){
            var expected="String.Contains: no argument was passed for 'input'.";
            
            var actual=Record.Exception(function(){
                String.Contains();
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfPatternIsOmitted(){
            var expected="String.Contains: no argument was passed for 'pattern'.";
            
            var actual=Record.Exception(function(){
                String.Contains("");
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsFalseIfInputIsUndefined(){
            var actual=String.Contains(undefined,"");
            
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfInputIsNull(){
            var actual=String.Contains(null,"");
            
            Assert.False(actual);
        }
        
        [Fact]
        function ReturnsFalseIfInputIsNotConvertibleToString(){
            var actual=String.Contains({toString:null},"");
            
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfActualIsUndefined(){
            var actual=String.Contains("",undefined);
            
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfActualIsNull(){
            var actual=String.Contains("",null);
            
            Assert.False(actual);
        }
        
        [Fact]
        function ReturnsFalseIfActualIsNotConvertibleToString(){
            var actual=String.Contains("",{toString:null});
            
            Assert.False(actual);
        }
        
        [Fact]
        function CallsToStringMethodOnInput(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.Contains(target,"");
            
            Assert.True(actual);
        }

        [Fact]
        function CallsToStringMethodOnPattern(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.Contains("",target);
            
            Assert.True(actual);
        }
        
        [Fact]
        function CallsToLowerCaseMethodOnInputIfIgnoreCaseSpecified(){
            var target={toString:function(){
                return {toLowerCase:function(){
                    actual=true;
                    return "";
                }};
            }};
            var actual=false;
            
            String.Contains(target,"",true);
            
            Assert.True(actual);
        }

        [Fact]
        function CallsToLowerCaseMethodOnPatternIfIgnoreCaseSpecified(){
            var target={toString:function(){
                return {toLowerCase:function(){
                    actual=true;
                    return "";
                }}
            }};
            var actual=false;
            
            String.Contains("",target,true);
            
            Assert.True(actual);
        }
        
        [Fact]
        function ReturnsFalseIfStringDoesNotContainPattern(){
            var actual=String.Contains("expected","pattern");
        
            Assert.False(actual);
        }

        [Fact]
        function ReturnsTrueIfStringContainsPattern(){
            var actual=String.Contains("expectedpattern","pattern");
        
            Assert.True(actual);
        }

        [Fact]
        function ReturnsFalseIfLowerCaseStringDoesNotContainLowerCasePattern(){
            var actual=String.Contains("ExpEcteD","PatTerN");
        
            Assert.False(actual);
        }

        [Fact]
        function ReturnsTrueIfLowerCaseStringContainsLowerCasePattern(){
            var actual=String.Contains("ExpEcteDPatTerN","PatTerN");
        
            Assert.True(actual);
        }
    }

    [Fixture]
    function Format(){
        [Fact]
        function ThrowsIfFormatIndexIsOutOfBounds(){
            var expected="String.Format: format match index was out of bounds at position [0].";
            
            var actual=Record.Exception(function(){
                String.Format("{0}");
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function IgnoresFormatStringThatIsNotANumber(){
            var actual=Record.Exception(function(){
                String.Format("{A}");
            });
            
            Assert.Null(actual);
        }
        
        [Fact]
        function ReplacesFormatStringWithProvidedArgument(){
            var expected="expected";
            
            var actual=String.Format("{0}",expected);
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReplacesFormatStringWithProvidedArguments(){
            var target="expected";
            var expected=[target,target,target].join('.');
            
            var actual=String.Format("{0}.{1}.{2}",target,target,target);
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ConvertsNullToNullString(){
            var expected="null";
            
            var actual=String.Format("{0}",null);
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ConvertsUndefinedToUndefinedString(){
            var expected="undefined";
            
            var actual=String.Format("{0}",undefined);
            
            Assert.Equal(expected,actual);
        }
    }

    [Fixture]
    function EndsWith(){
        [Fact]
        function ThrowsIfInputIsOmitted(){
            var expected="String.EndsWith: no argument was provided for 'input'.";
            
            var actual=Record.Exception(function(){
                String.EndsWith();
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfPatternIsOmitted(){
            var expected="String.EndsWith: no argument was provided for 'pattern'.";
            
            var actual=Record.Exception(function(){
                String.EndsWith("");
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsFalseIfInputIsUndefined(){
            var actual=String.EndsWith(undefined,"");
        
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfInputIsNull(){
            var actual=String.EndsWith(null,"");
        
            Assert.False(actual);
        }
        
        [Fact]
        function ReturnsFalseIfInputToStringMethodIsNotAValidFunction(){
            var actual=String.EndsWith({toString:null},"");
        
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfPatternIsUndefined(){
            var actual=String.EndsWith("",undefined);
        
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfPatternIsNull(){
            var actual=String.EndsWith("",null);
        
            Assert.False(actual);
        }
        
        [Fact]
        function ReturnsFalseIfPatternToStringMethodIsNotAValidFunction(){
            var actual=String.EndsWith("",{toString:null});
        
            Assert.False(actual);
        }
        
        [Fact]
        function CallsToStringMethodOnInput(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.EndsWith(target,"");
        
            Assert.True(actual);
        }

        [Fact]
        function CallsToStringMethodOnPattern(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.EndsWith("",target);
        
            Assert.True(actual);
        }

        [Fact]
        function CallsToLowerCaseMethodOnInputIfIgnoreCaseIsSpecified(){
            var target={toString:function(){
                return {toLowerCase:function(){
                   actual=true;
                   return "";
                }};
            }}
            var actual=false;
            
            String.EndsWith(target,"",true);
        
            Assert.True(actual);
        }

        [Fact]
        function CallsToLowerCaseMethodOnPatternIfIgnoreCaseIsSpecified(){
            var target={toString:function(){
                return {toLowerCase:function(){
                   actual=true;
                   return "";
                }};
            }}
            var actual=false;
            
            String.EndsWith("",target,true);
        
            Assert.True(actual);
        }
        
        [Fact]
        function ReturnsFalseIfInputDoesNotEndWithPattern(){
            var actual=String.EndsWith("ExpectedPattern","pattern");
            
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfLowerCaseInputDoesNotEndWithLowerCasePattern(){
            var actual=String.EndsWith("PatternExpected","Pattern");
            
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfInputDoesNotEndWithPatternOfGreaterLength(){
            var actual=String.EndsWith("patter","pattern");
            
            Assert.False(actual);
        }

        [Fact]
        function ReturnsTrueIfInputEndsWithPattern(){
            var actual=String.EndsWith("ExpectedPattern","Pattern");
            
            Assert.True(actual);
        }

        [Fact]
        function ReturnsTrueIfLowerCaseInputEndsWithLowerCasePattern(){
            var actual=String.EndsWith("ExpectedpAtTeRn","PaTtErN",true);
            
            Assert.True(actual);
        }
    }

    [Fixture]
    function Equals(){
        [Fact]
        function ThrowsIfExpectedIsOmitted(){
            var expected="String.Equals: 'expected' must contain a valid String.";
            
            var actual=Record.Exception(function(){
                String.Equals();
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfExpectedIsUndefined(){
            var expected="String.Equals: 'expected' must contain a valid String.";
            
            var actual=Record.Exception(function(){
                String.Equals(undefined);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfExpectedIsNull(){
            var expected="String.Equals: 'expected' must contain a valid String.";
            
            var actual=Record.Exception(function(){
                String.Equals(null);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CoercesActualToStringUndefinedIfOmitted(){
            var targetToString=String.prototype&&String.prototype.toString;
            String.prototype.toString=function(){
                if(this!="expected")actual=this;
            }
            var actual=false;
            var expected="undefined";

            String.Equals("expected");
            if(targetToString)String.prototype.toString=targetToString;
        
            Assert.Equal(expected,actual);
        }

        [Fact]
        function CoercesActualToStringUndefinedIfUndefined(){
            var targetToString=String.prototype&&String.prototype.toString;
            String.prototype.toString=function(){
                if(this!="expected")actual=this;
            }
            var actual=false;
            var expected="undefined";

            String.Equals("expected",undefined);
            if(targetToString)String.prototype.toString=targetToString;
        
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CoercesActualToStringNullIfNull(){
            var targetToString=String.prototype&&String.prototype.toString;
            String.prototype.toString=function(){
                if(this!="expected")actual=this;
            }
            var actual=false;
            var expected="null";

            String.Equals("expected",null);
            if(targetToString)String.prototype.toString=targetToString;
        
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfExpectedIsNotConvertibleToString(){
            var target=new String();
            target.toString=null;
            var expected="String.Equals: 'expected' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.Equals(target,"");
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfActualIsNotConvertibleToString(){
            var target={toString:null};
            var expected="String.Equals: 'actual' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.Equals("",target);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsFalseIfExpectedDoesNotMatchActual(){
            var actual=String.Equals("expected","actual");
            
            Assert.False(actual);
        }

        [Fact]
        function SetsReasonIfExpectedDoesNotMatchActual(){
            var targetExpected="expected";
            var targetActual="actual";
            var expected=String.Format("String.Equals: Strings did not match. Expected '{0}', found '{1}'.",targetExpected,targetActual);
            var reason={};
            
            String.Equals(targetExpected,targetActual,reason);
            var actual=reason.Value;
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsTrueIfExpectedAndActualMatch(){
            var actual=String.Equals("expected","expected");
        
            Assert.True(actual);
        }
    }

    [Fixture]
    function IsEmpty(){
        [Fact]
        function ThrowsIfInputIsNotAString(){
            var expected="String.IsEmpty: 'input' must contain a valid String.";

            var actual=Record.Exception(function(){
                String.IsEmpty({});
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsFalseIfStringHasValue(){
            var target="target";

            var actual=String.IsEmpty(target);

            Assert.False(actual);
        }

        [Fact]
        function ReturnsTrueIfStringHasNoValue(){
            var target="";

            var actual=String.IsEmpty(target);

            Assert.True(actual);
        }
    }

    [Fixture]
    function Pad(){
        [Fact]
        function ThrowsIfInputIsNull(){
            var expected="String.Pad: 'input' must be convertible to String.";

            var actual=Record.Exception(function(){
                String.Pad(null,'',0);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfInputIsNotConvertibleToString(){
            var expected="String.Pad: 'input' must be convertible to String.";

            var actual=Record.Exception(function(){
                String.Pad({toString:null},'',0);
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function DoesNotThrowIfInputIsZero(){
            var expected="String.Pad: 'input' must be convertible to String.";

            var actual=Record.Exception(function(){
                String.Pad(0,'',0);
            });

            Assert.Null(actual);        
        }

        [Fact]
        function DoesNotThrowIfInputIsFalse(){
            var expected="String.Pad: 'input' must be convertible to String.";

            var actual=Record.Exception(function(){
                String.Pad(false,'',0);
            });

            Assert.Null(actual);        
        }
        
        [Fact]
        function ThrowsIfPatternIsNull(){
            var expected="String.Pad: 'pattern' must be convertible to String.";

            var actual=Record.Exception(function(){
                String.Pad('',null);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfPatternIsNotConvertibleToString(){
            var expected="String.Pad: 'pattern' must be convertible to String.";

            var actual=Record.Exception(function(){
                String.Pad('',{toString:null});
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function DoesNotThrowIfPatternIsZero(){
            var expected="String.Pad: 'pattern' must be convertible to String.";

            var actual=Record.Exception(function(){
                String.Pad('',0,0);
            });

            Assert.Null(actual);        
        }

        [Fact]
        function DoesNotThrowIfPatternIsFalse(){
            var expected="String.Pad: 'pattern' must be convertible to String.";

            var actual=Record.Exception(function(){
                String.Pad('',false,0);
            });

            Assert.Null(actual);        
        }

        [Fact]
        function ThrowsIfMinimumLengthIsNotANumber(){
            var expected="String.Pad: 'minimumLength' must be a valid Number.";
            
            var actual=Record.Exception(function(){
                String.Pad('','',null);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallsToStringOnInput(){
            var targetInput={toString:function(){
                actual=true;
                return '';
            }};
            var actual=false;
                       
            String.Pad(targetInput,'',1);
            
            Assert.True(actual);
        }

        [Fact]
        function CallsToStringOnPattern(){
            var targetPattern={toString:function(){
                actual=true;
                return '';
            }};
            var actual=false;
                       
            String.Pad('',targetPattern,1);
            
            Assert.True(actual);
        }
        
        [Fact]
        function ReturnsInputIfInputMatchesMinimumLength(){
            var expected="expected";
        
            var actual=String.Pad(expected,'pattern',expected.length);
        
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsInputIfInputExceedsMinimumLength(){
            var expected="expected";
        
            var actual=String.Pad(expected,'pattern',0);
        
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsInputIfPatternIsEmpty(){
            var expected="expected";
        
            var actual=String.Pad(expected,'',expected.length+1);
        
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsPatternPrependedToInput(){
            var targetInput="target";
            var targetPattern="pattern";
            var expected=targetPattern+targetInput;
            
            var actual=String.Pad(targetInput,targetPattern,targetInput.length+targetPattern.length);
        
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsPatternRepeatedlyPrependedToInput(){
            var targetInput="target";
            var targetPattern="pattern";
            var expected=targetPattern+targetPattern+targetPattern+targetInput;
            
            var actual=String.Pad(targetInput,targetPattern,targetInput.length+(targetPattern.length*3));
        
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsUnbrokenPatternWhenMinimumLengthIsExceeded(){
            var targetInput="target";
            var targetPattern="pattern";
            var expected=targetPattern+targetPattern+targetInput;
            
            var actual=String.Pad(targetInput,targetPattern,targetInput.length+targetPattern.length+1);
        
            Assert.Equal(expected,actual);
        }
    }

    [Fixture]
    function StartsWith(){
        [Fact]
        function ThrowsIfInputIsOmitted(){
            var expected="String.StartsWith: no argument was provided for 'input'.";
            
            var actual=Record.Exception(function(){
                String.StartsWith();
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfPatternIsOmitted(){
            var expected="String.StartsWith: no argument was provided for 'pattern'.";
            
            var actual=Record.Exception(function(){
                String.StartsWith("");
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsFalseIfInputIsUndefined(){
            var actual=String.StartsWith(undefined,"");
        
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfInputIsNull(){
            var actual=String.StartsWith(null,"");
        
            Assert.False(actual);
        }
        
        [Fact]
        function ReturnsFalseIfInputToStringMethodIsNotAValidFunction(){
            var actual=String.StartsWith({toString:null},"");
        
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfPatternIsUndefined(){
            var actual=String.StartsWith("",undefined);
        
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfPatternIsNull(){
            var actual=String.StartsWith("",null);
        
            Assert.False(actual);
        }
        
        [Fact]
        function ReturnsFalseIfPatternToStringMethodIsNotAValidFunction(){
            var actual=String.StartsWith("",{toString:null});
        
            Assert.False(actual);
        }
        
        [Fact]
        function CallsToStringMethodOnInput(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.StartsWith(target,"");
        
            Assert.True(actual);
        }

        [Fact]
        function CallsToStringMethodOnPattern(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.StartsWith("",target);
        
            Assert.True(actual);
        }

        [Fact]
        function CallsToLowerCaseMethodOnInputIfIgnoreCaseIsSpecified(){
            var target={toString:function(){
                return {toLowerCase:function(){
                   actual=true;
                   return "";
                }};
            }}
            var actual=false;
            
            String.StartsWith(target,"",true);
        
            Assert.True(actual);
        }

        [Fact]
        function CallsToLowerCaseMethodOnPatternIfIgnoreCaseIsSpecified(){
            var target={toString:function(){
                return {toLowerCase:function(){
                   actual=true;
                   return "";
                }};
            }}
            var actual=false;
            
            String.StartsWith("",target,true);
        
            Assert.True(actual);
        }
        
        [Fact]
        function ReturnsFalseIfInputDoesNotStartWithPattern(){
            var actual=String.StartsWith("ExpectedPattern","expected");
            
            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfLowerCaseInputDoesNotStartWithLowerCasePattern(){
            var actual=String.StartsWith("PatternExpected","Expected");
            
            Assert.False(actual);
        }

        [Fact]
        function ReturnsTrueIfInputStartsWithPattern(){
            var actual=String.StartsWith("ExpectedPattern","Expected");
            
            Assert.True(actual);
        }

        [Fact]
        function ReturnsTrueIfLowerCaseInputStartsWithLowerCasePattern(){
            var actual=String.StartsWith("ExPeCtEdpAtTeRn","eXpEcTeD",true);
            
            Assert.True(actual);
        }
    }

    [Fixture]
    function Trim(){
        [Fact]
        function ThrowsIfInputIsOmitted(){
            var expected="String.Trim: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.Trim();
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsUndefined(){
            var expected="String.Trim: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.Trim(undefined);
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsNull(){
            var expected="String.Trim: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.Trim(null);
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsNotConvertibleToString(){
            var expected="String.Trim: 'input' must be convertible to String.";
            var target={toString:null};
            
            var actual=Record.Exception(function(){
                String.Trim(target);
            });

            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallsToStringMethodOnInput(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.Trim(target);
            
            Assert.True(actual);
        }

        [Fact]
        function DoesNotThrowIfCharIsOmitted(){
            var actual=Record.Exception(function(){
                String.Trim("");
            });
            
            Assert.Null(actual);
        }

        [Fact]
        function ThrowsIfCharIsUndefined(){
            var expected=String.Format("String.Trim: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.Trim("",undefined);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfCharIsNull(){
            var expected=String.Format("String.Trim: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.Trim("",null);
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfCharIsNotConvertibleToString(){
            var target={toString:null};
            var expected=String.Format("String.Trim: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.Trim("",target);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallsToStringMethodOnChar(){
            var target={toString:function(){
                actual=true;
            }};
            var actual=false;
            
            String.Trim("",target);
            
            Assert.True(actual);
        }
        
        [Fact]
        function RemovesCharFromBeginningOfInput(){
            var expected="expected";
            var target='a';
            
            var actual=String.Trim(target+expected,target);
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCharsFromBeginningOfInput(){
            var expected="expected";
            var targets=['a','b'];
            
            var actual=String.Trim(targets[0]+targets[1]+expected,targets[0],targets[1]);
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCharFromEndOfInput(){
            var expected="expected";
            var target='a';
            
            var actual=String.Trim(expected+target,target);
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCharsFromEndOfInput(){
            var expected="expected";
            var targets=['a','b'];
            
            var actual=String.Trim(expected+targets[0]+targets[1],targets[0],targets[1]);
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function RemovesWhitespaceFromStartOfInput(){
            var expected="expected";
            
            var actual=String.Trim("     "+expected);

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesTabFromStartOfInput(){
            var expected="expected";
            
            var actual=String.Trim("	"+expected);

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCarriageReturnFromStartOfInput(){
            var expected="expected";
            
            var actual=String.Trim("\n"+expected);

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesWhitespaceFromEndOfInput(){
            var expected="expected";
            
            var actual=String.Trim(expected+"     ");

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesTabFromEndOfInput(){
            var expected="expected";
            
            var actual=String.Trim(expected+"	");

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCarriageReturnFromEndOfInput(){
            var expected="expected";
            
            var actual=String.Trim(expected+"\n");

            Assert.Equal(expected,actual);
        }
    }

    [Fixture]
    function TrimStart(){
        [Fact]
        function ThrowsIfInputIsOmitted(){
            var expected="String.TrimStart: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.TrimStart();
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsUndefined(){
            var expected="String.TrimStart: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.TrimStart(undefined);
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsNull(){
            var expected="String.TrimStart: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.TrimStart(null);
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsNotConvertibleToString(){
            var expected="String.TrimStart: 'input' must be convertible to String.";
            var target={toString:null};
            
            var actual=Record.Exception(function(){
                String.TrimStart(target);
            });

            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallsToStringMethodOnInput(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.TrimStart(target);
            
            Assert.True(actual);
        }

        [Fact]
        function DoesNotThrowIfCharIsOmitted(){
            var actual=Record.Exception(function(){
                String.TrimStart("");
            });
            
            Assert.Null(actual);
        }

        [Fact]
        function ThrowsIfCharIsUndefined(){
            var expected=String.Format("String.TrimStart: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.TrimStart("",undefined);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfCharIsNull(){
            var expected=String.Format("String.TrimStart: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.TrimStart("",null);
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfCharIsNotConvertibleToString(){
            var target={toString:null};
            var expected=String.Format("String.TrimStart: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.TrimStart("",target);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallsToStringMethodOnChar(){
            var target={toString:function(){
                actual=true;
            }};
            var actual=false;
            
            String.TrimStart("",target);
            
            Assert.True(actual);
        }
        
        [Fact]
        function RemovesCharFromBeginningOfInput(){
            var expected="expected";
            var target='a';
            
            var actual=String.TrimStart(target+expected,target);
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCharsFromBeginningOfInput(){
            var expected="expected";
            var targets=['a','b'];
            
            var actual=String.TrimStart(targets[0]+targets[1]+expected,targets[0],targets[1]);
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function RemovesWhitespaceFromStartOfInput(){
            var expected="expected";
            
            var actual=String.TrimStart("     "+expected);

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesTabFromStartOfInput(){
            var expected="expected";
            
            var actual=String.TrimStart("	"+expected);

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCarriageReturnFromStartOfInput(){
            var expected="expected";
            
            var actual=String.TrimStart("\n"+expected);

            Assert.Equal(expected,actual);
        }
    }

    [Fixture]
    function TrimEnd(){
        [Fact]
        function ThrowsIfInputIsOmitted(){
            var expected="String.TrimEnd: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.TrimEnd();
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsUndefined(){
            var expected="String.TrimEnd: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.TrimEnd(undefined);
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsNull(){
            var expected="String.TrimEnd: 'input' must be convertible to String.";
            
            var actual=Record.Exception(function(){
                String.TrimEnd(null);
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInputIsNotConvertibleToString(){
            var expected="String.TrimEnd: 'input' must be convertible to String.";
            var target={toString:null};
            
            var actual=Record.Exception(function(){
                String.TrimEnd(target);
            });

            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallsToStringMethodOnInput(){
            var target={toString:function(){
                actual=true;
                return "";
            }}
            var actual=false;
            
            String.TrimEnd(target);
            
            Assert.True(actual);
        }

        [Fact]
        function DoesNotThrowIfCharIsOmitted(){
            var actual=Record.Exception(function(){
                String.TrimEnd("");
            });
            
            Assert.Null(actual);
        }

        [Fact]
        function ThrowsIfCharIsUndefined(){
            var expected=String.Format("String.TrimEnd: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.TrimEnd("",undefined);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfCharIsNull(){
            var expected=String.Format("String.TrimEnd: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.TrimEnd("",null);
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfCharIsNotConvertibleToString(){
            var target={toString:null};
            var expected=String.Format("String.TrimEnd: char at position [{0}] was not convertible to String.",0);
            
            var actual=Record.Exception(function(){
                String.TrimEnd("",target);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallsToStringMethodOnChar(){
            var target={toString:function(){
                actual=true;
            }};
            var actual=false;
            
            String.TrimEnd("",target);
            
            Assert.True(actual);
        }
        
        [Fact]
        function RemovesCharFromEndOfInput(){
            var expected="expected";
            var target='a';
            
            var actual=String.TrimEnd(expected+target,target);
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCharsFromEndOfInput(){
            var expected="expected";
            var targets=['a','b'];
            
            var actual=String.TrimEnd(expected+targets[0]+targets[1],targets[0],targets[1]);
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function RemovesWhitespaceFromEndOfInput(){
            var expected="expected";
            
            var actual=String.TrimEnd(expected+"     ");

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesTabFromEndOfInput(){
            var expected="expected";
            
            var actual=String.TrimEnd(expected+"	");

            Assert.Equal(expected,actual);
        }

        [Fact]
        function RemovesCarriageReturnFromEndOfInput(){
            var expected="expected";
            
            var actual=String.TrimEnd(expected+"\n");

            Assert.Equal(expected,actual);
        }
    }
}